﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ClassGeneratorHelper.cs" company="Pierhouse ">
//   Copyright 2011 IMS
// </copyright>
// <summary>
//   ClassGeneratorHelper
// </summary>
// <author>Thiru </author>
// <history>12/10/2012 </history>
// --------------------------------------------------------------------------------------------------------------------

#region usings

#endregion usings

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace SimpleCodeGen
{
    /// <summary>
    /// ClassGeneratorHelper
    /// </summary>
    public class ClassGeneratorHelper
    {
        #region public methods

        /// <summary>
        /// Generates the business object class.
        /// </summary>
        /// <param name="columns">The columns.</param>
        /// <param name="customParameter">The custom parameter.</param>
        /// <returns></returns>
        public static bool GenerateBusinessObjectClass(IEnumerable<CustomColumn> columns, CustomParameter customParameter)
        {
            var result = false;
            try
            {
                if (columns != null && columns.Count() > 0)
                {
                    var template = GetTemplate(ConstantHelper.BusinessObjectCodeTemplateName);
                    var propertyTemplate = GetTemplate(ConstantHelper.PropertyCodeTemplateName);
                    if (!string.IsNullOrEmpty(template) && !string.IsNullOrEmpty(propertyTemplate))
                    {
                        // generate property string
                        var properties = GenerateClassProperties(propertyTemplate, columns);

                        // dictionary  for business object
                        var tokens = GetBusinessObjectTokens(customParameter, properties);

                        // parse
                        var output = ParseTemplate(tokens, template);

                        // generate code
                        // create directory
                        result = GenerateOutput(output, customParameter, CodeGenType.BusinessAccessLayer, ClassType.CS, customParameter.ClassName);
                    }
                }
            }
            catch (Exception exception)
            {
            }
            return result;
        }

        /// <summary>
        /// Generates the data provider interface class.
        /// </summary>
        /// <param name="columns">The columns.</param>
        /// <param name="customParameter">The custom parameter.</param>
        /// <returns></returns>
        public static bool GenerateDataProviderInterfaceClass(IEnumerable<CustomColumn> columns, CustomParameter customParameter)
        {
            var result = false;
            try
            {
                if (columns != null && columns.Count() > 0)
                {
                    var template = GetTemplate(ConstantHelper.DataProviderInterfaceTemplateName);
                    if (!string.IsNullOrEmpty(template))
                    {
                        // dictionary  for business object
                        var tokens = GetParameterTokens(customParameter, columns);

                        // parse
                        var output = ParseTemplate(tokens, template);

                        // generate filename
                        var fileName = string.Format("I{0}Repository", customParameter.ClassName);

                        // create directory
                        result = GenerateOutput(output, customParameter, CodeGenType.DataAccessLayer, ClassType.CS, fileName);
                    }
                }
            }
            catch (Exception exception)
            {
            }
            return result;
        }

        #endregion public methods

        #region private methods

        /// <summary>
        /// Gets the business object template.
        /// </summary>
        /// <returns></returns>
        private static string GetTemplate(string templateName)
        {
            var result = string.Empty;
            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(templateName))
            {
                if (stream != null)
                {
                    using (var reader = new StreamReader(stream))
                    {
                        result = reader.ReadToEnd();
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Gets the parameter tokens.
        /// </summary>
        /// <param name="customParameter">The custom parameter.</param>
        /// <returns></returns>
        private static Dictionary<string, string> GetParameterTokens(CustomParameter customParameter, IEnumerable<CustomColumn> columns = null)
        {
            var result = new Dictionary<string, string>();

            if (customParameter != null)
            {
                result.Add("#COMPANY_NAME#", customParameter.CompanyName);
                result.Add("#CLASS_NAME#", customParameter.ClassName);
                result.Add("#CREATE_DATE#", DateTime.Now.ToLongDateString());
                result.Add("#COPYRIGHT#", customParameter.Copyright);
                result.Add("#DEVELOPER_NAME#", customParameter.DeveloperName);

                // namespaces
                result.Add("#NAMESPACE_BUSINESS_OBJECT#", customParameter.NamespaceBusinessObject);
                result.Add("#NAMESPACE_DATAPROVIDER_INTERFACE#", customParameter.NamespaceDataProviderInterface);

                // any process
                result.Add("#PRIMARY_KEYES_PARAMETER#", GetPrimaryKeysWithDataType(columns));
            }
            return result;
        }

        /// <summary>
        /// Gets the property tokens.
        /// </summary>
        /// <param name="customColumn">The custom column.</param>
        /// <returns></returns>
        private static Dictionary<string, string> GetPropertyTokens(CustomColumn customColumn)
        {
            var result = new Dictionary<string, string>();

            if (customColumn != null)
            {
                result.Add("#PROPERTY_NAME#", customColumn.ColumnName);
                result.Add("#DATA_TYPE#", ApplyNullableDatatype(customColumn.DataType));
            }
            return result;
        }

        /// <summary>
        /// Gets the business object tokens.
        /// </summary>
        /// <param name="customParameter">The custom parameter.</param>
        /// <param name="properties">The properties.</param>
        /// <returns></returns>
        private static Dictionary<string, string> GetBusinessObjectTokens(CustomParameter customParameter, string properties)
        {
            var result = new Dictionary<string, string>();

            if (customParameter != null && !string.IsNullOrEmpty(properties))
            {
                result.Add("#CLASS_GENERATED_PROPERTIES#", properties);
                result = result.Union(GetParameterTokens(customParameter)).ToDictionary(pair => pair.Key, pair => pair.Value);
            }
            return result;
        }

        /// <summary>
        /// Parses the template.
        /// </summary>
        /// <param name="tokens">The tokens.</param>
        /// <param name="template">The template.</param>
        /// <returns></returns>
        private static string ParseTemplate(IDictionary<string, string> tokens, string template)
        {
            var result = template;

            if (tokens != null)
            {
                foreach (var token in tokens)
                {
                    result = result.Replace(token.Key, token.Value);
                }
            }
            return result;
        }

        /// <summary>
        /// Generates the directories.
        /// </summary>
        private static string GenerateDirectories(CustomParameter customParameter, CodeGenType type)
        {
            var result = string.Empty;

            if (customParameter != null && !string.IsNullOrEmpty(customParameter.OutputFolderPath))
            {
                if (Directory.Exists(customParameter.OutputFolderPath))
                {
                    var outputFolder = customParameter.OutputFolderPath;
                    if (!outputFolder.EndsWith("\\"))
                    {
                        outputFolder += "\\";
                    }

                    switch (type)
                    {
                        case CodeGenType.BusinessAccessLayer:
                            outputFolder += "BAL";
                            break;
                        case CodeGenType.DataAccessLayer:
                            outputFolder += "DAL";
                            break;
                        case CodeGenType.MVCLayer:
                            outputFolder += "MVC";
                            break;
                    }

                    // create if not exists
                    if (!Directory.Exists(outputFolder))
                    {
                        Directory.CreateDirectory(outputFolder);
                    }

                    result = outputFolder;
                }
            }

            return result;
        }

        /// <summary>
        /// Generates the output.
        /// </summary>
        /// <param name="customParameter">The custom parameter.</param>
        /// <param name="codeGenType">Type of the code gen.</param>
        /// <param name="classType">Type of the class.</param>
        /// <returns></returns>
        private static bool GenerateOutput(string code, CustomParameter customParameter, CodeGenType codeGenType, ClassType classType, string fileName)
        {
            var result = false;

            var folder = GenerateDirectories(customParameter, codeGenType);
            if (!string.IsNullOrEmpty(folder) && !string.IsNullOrEmpty(code) && Directory.Exists(folder))
            {
                string extension = string.Empty;
                switch (classType)
                {
                    case ClassType.CS:
                        extension = "cs";
                        break;
                    case ClassType.SQL:
                        extension = "sql";
                        break;
                    case ClassType.VB:
                        extension = "vb";
                        break;
                }

                // save file
                using (var outfile = new StreamWriter(string.Format("{0}\\{1}.{2}", folder, fileName, extension)))
                {
                    outfile.Write(code);
                }

                result = true;
            }

            return result;
        }

        /// <summary>
        /// Generates the class properties.
        /// </summary>
        /// <param name="template">The template.</param>
        /// <param name="columns">The columns.</param>
        /// <returns></returns>
        private static string GenerateClassProperties(string template, IEnumerable<CustomColumn> columns)
        {
            var properties = new StringBuilder();

            if (!string.IsNullOrEmpty(template) && columns != null && columns.Count() > 0)
            {
                foreach (var customColumn in columns)
                {
                    properties.AppendLine(ParseTemplate(GetPropertyTokens(customColumn), template));
                }
            }
            return properties.ToString();
        }

        /// <summary>
        /// Determines whether [is nullable column] [the specified datatype].
        /// </summary>
        /// <param name="datatype">The datatype.</param>
        /// <returns>
        ///   <c>true</c> if [is nullable column] [the specified datatype]; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsNullableColumn(string datatype)
        {
            var result = true;
            if (!string.IsNullOrEmpty(datatype))
            {
                if (datatype.ToLower().Contains("string") || datatype.ToLower().Contains("byte"))
                {
                    result = false;
                }
            }
            return result;
        }

        /// <summary>
        /// Applies the nullable datatype.
        /// </summary>
        /// <param name="datatype">The datatype.</param>
        /// <returns></returns>
        private static string ApplyNullableDatatype(string datatype)
        {
            return IsNullableColumn(datatype) ? datatype + "?" : datatype;
        }

        /// <summary>
        /// Gets the type of the primary keys with data.
        /// </summary>
        /// <param name="columns">The columns.</param>
        /// <returns></returns>
        private static string GetPrimaryKeysWithDataType(IEnumerable<CustomColumn> columns)
        {
            var result = string.Empty;

            if (columns != null && columns.Count() > 0)
            {
                foreach (var customColumn in columns)
                {
                    if (customColumn.IsKey)
                    {
                        result = result + string.Format(" {0} {1} ,", ApplyNullableDatatype(customColumn.DataType), customColumn.ColumnName);
                    }
                }

                // remove "," at the end of string
                if (!string.IsNullOrEmpty(result))
                {
                    result = result.Substring(0, result.LastIndexOf(","));
                }
            }

            return result;
        }

        #endregion private methods
    }
}